import { Component, DoCheck, ElementRef, KeyValueDiffers, OnChanges, OnDestroy, AfterContentInit,OnInit, Renderer2, Input, Output, EventEmitter, ViewChild, SimpleChanges } from '@angular/core';
import { DocumentWrapper, WindowWrapper } from '../shared/services';
import { TableColumn, TableColumnDataItem, WidthItem, TableSelectEvent, ElTableSlotEvent } from './table.interface';
import { __assign } from 'tslib';
import { ElTableFormat } from './utils/format';

@Component({
  selector: 'el-table',
  templateUrl: './table.component.html',
})
export class TableComponent implements AfterContentInit, OnDestroy, OnChanges, DoCheck {
  private el: any;
  private renderer: any;
  private document: any;
  private window: any;
  private differs: any;
  private columns: any=[];
  private globalListenFunc: any;
  private orderMap: any;
  private modelStorge: any;
  private differ: any;
  private widthCount: any = 0;
  @ViewChild('headerRef') headerRef!: ElementRef;
  @Input() columnsData: TableColumnDataItem[] = [];
  @Input() columnsWithLevel: any = [];
  @Input() layout: any = {
    headerHeight: 40,
    bodyHeight: 'auto',
    bodyWidth: 'auto',
    scrollBarWidth: 0,
  };
  @Input() columnsWidth: any = [];
  @Input() height: string | number = 'auto';
  @Input() size: string  = '';
  @Input() placeholder: string = '';
  @Input() stripe: boolean = false;
  @Input() fit: boolean = true;
  @Input() border: boolean = false;
  @Input() scrollX: boolean = false;
  @Input() showHeader: boolean = true;
  @Input() isGroup: boolean = false;
  @Input() center: string = '';
  @Input() rowClassName!: (userRow: any, index: number) => string;
  @Input() model: any;
  @Output() modelChange: EventEmitter<any> = new EventEmitter();
  @Output() select: EventEmitter<TableSelectEvent> = new EventEmitter();
  @Output() selectAll: EventEmitter<TableSelectEvent> = new EventEmitter();
  @Output() selectionChange: EventEmitter<TableSelectEvent> = new EventEmitter();
  @Output() cellMouseEnter: EventEmitter<Event> = new EventEmitter();
  @Output() cellMouseLeave: EventEmitter<Event> = new EventEmitter();
  @Output() cellClick: EventEmitter<ElTableSlotEvent> = new EventEmitter();
  @Output() cellDblclick: EventEmitter<ElTableSlotEvent> = new EventEmitter();
  constructor(el: ElementRef, renderer: Renderer2, document: DocumentWrapper, window: WindowWrapper, differs: KeyValueDiffers) {
    this.el = el;
    this.renderer = renderer;
    this.document = document;
    this.window = window;
    this.differs = differs;
    this.differ = this.differs.find([]).create();
  }
  ngOnDestroy(): void {
    this.globalListenFunc && this.globalListenFunc();
  }
  ngDoCheck(): void {
    var _this = this;
    var /** @type {?} */ change = this.differ.diff(this.model);
    if (!change || !this.orderMap)
      return;
    // distribution template
    this.modelStorge = this.model;
    var /** @type {?} */ nextColumns = this.columns.map( (column:any) => {
      if (!column.slot)
        return column;
      var /** @type {?} */ TEMPLATE_KEY = this.GEN_TEMPLATE_KEY();
      _this.modelStorge = _this.model.map(function (v:any) {
        var _a:any;
        return Object.assign(v, (_a = {}, _a[TEMPLATE_KEY] = column.slot, _a));
       
      });
      return Object.assign(column, { prop: TEMPLATE_KEY });
    });
    this.orderMap = nextColumns.reduce(function (pre:any, next:any) {
      var _a:any;
      return Object.assign(pre, (_a = {}, _a[next.prop] = next, _a));
      
    }, {});
    this.transformModelData();
  }


  ngAfterContentInit(): void {
    var _this = this;
    this.updateLayout();
    this.updateBodyHeight();
    this.globalListenFunc = this.renderer.listen('window', 'resize', function () {
      _this.updateLayout();
      _this.columnsWidth = _this.computeColumnsWidth(_this.columnsWidth);
    });
  }

  GEN_TEMPLATE_KEY() {
    return Math.random().toString(16).substr(2, 8);
  }

  bodyScroll(event: any) {
    if (!this.scrollX)
      return;
    var /** @type {?} */ el = ((event.target));
    if (el.scrollLeft === undefined)
      return;
    this.headerRef.nativeElement.scrollLeft = el.scrollLeft;
  }

  updateColumns(column: any) {
    var /** @type {?} */ next = Object.assign(column, { index: this.columns.length });
    this.columns.push(next);
  }

  updateBodyHeight() {
    var _this =  this;
    // var /** @type {?} */ height = ElTableFormat.getCSSValue(_this.height);
    //   var /** @type {?} */ header = _this.headerRef.nativeElement;
 
    //   if (!header || height === 0) {
    //     // clearTimeout(timer);
    //     return;
    //   }
       
    //   var /** @type {?} */ headerHeight = header.offsetHeight;
    //   if (headerHeight) {
    //     _this.layout.headerHeight = headerHeight;
    //     _this.layout.bodyHeight = height - _this.layout.headerHeight;
    //     _this.layout.scrollBarWidth = _this.window.innerWidth - _this.document.body.clientWidth;
    //   }
    //   return;
    var /** @type {?} */ timer = setTimeout( ()=> {
      var /** @type {?} */ height = ElTableFormat.getCSSValue(_this.height);
      var /** @type {?} */ header = _this.headerRef.nativeElement;
 
      if (!header || height === 0) {
        clearTimeout(timer);
        return;
      }
       
      var /** @type {?} */ headerHeight = header.offsetHeight;
      if (headerHeight) {
        _this.layout.headerHeight = headerHeight;
        _this.layout.bodyHeight = height - _this.layout.headerHeight;
        _this.layout.scrollBarWidth = _this.window.innerWidth - _this.document.body.clientWidth;
      }
      clearTimeout(timer);
    }, 500);
  }

  updateLayout() {
    var /** @type {?} */ elTable = this.el.nativeElement.children[0];
    this.layout.bodyWidth = this.widthCount || elTable.clientWidth;
    if (this.widthCount) {
      this.renderer.setStyle(elTable, 'width', this.widthCount + "px");
    }
  }

  updateColumnsWidth(widthItem: any) {
    this.columnsWidth.push(widthItem);
  }

  computeColumnsWidth(columnsWidth: any) {
    var /** @type {?} */ auto = 0, /** @type {?} */ count = 0;
    columnsWidth.forEach((item: any)=>{
      if (item.auto)
        return auto++;
      if (Number.isNaN(item.width)) {
        item.auto = true;
        return auto++;
      }
      count += item.width;
      return item;
    });
    // if user has set the width, use fixed width
    // update layout
    if (!auto) {
      this.widthCount = count;
      this.updateLayout();
    }
    var /** @type {?} */ average = (this.layout.bodyWidth - count) / auto;
    return columnsWidth.map(function (item: any) {
      return item.auto ? Object.assign(item, { width: average }) : item;
    });
  }


  transformColumnsData() {
    var _this = this;
    // order by deep
    this.columns = this.columns.map(function (column:any) {
      if (!Array.isArray(_this.columnsWithLevel[column.level])) {
        _this.columnsWithLevel[column.level] = [];
      }
      _this.columnsWithLevel[column.level].push(column);
      if (column.deep === 0)
        return column;
      return null;
    }).filter(function (r:any) { return r; });
    this.columnsWithLevel.reverse();
    this.columnsWidth = this.computeColumnsWidth(this.columnsWidth);
    // distribution template
    this.columns = this.columns.map( (column:any) => {
      if (!column.slot)
        return column;
      var /** @type {?} */ TEMPLATE_KEY = this.GEN_TEMPLATE_KEY();
      _this.modelStorge = _this.model.map(function (v:any) {
        var _a:any;
        return Object.assign(v, (_a = {}, _a[TEMPLATE_KEY] = column.slot, _a));
        
      });
      return Object.assign(column, { prop: TEMPLATE_KEY });
    });
    this.orderMap = this.columns.reduce(function (pre:any, next:any) {
      var _a:any;
      return Object.assign(pre, (_a = {}, _a[next.prop] = next, _a));
    }, {});
    this.transformModelData();
  }

  transformModelData() {
    var /** @type {?} */ orderMap = this.orderMap;
    // add index, width, value
    if (!this.modelStorge) {
      this.modelStorge = this.model;
    }
    var /** @type {?} */ modelWithIndex = this.modelStorge.map(function (row:any) {
      return Object.keys(__assign({}, row, orderMap)).map(function (v:any) {
        var /** @type {?} */ item = orderMap[v] || {};
        var _a:any={};
          _a[v] = row[v],
          _a.index = item.index,
          _a.width = item.width,
          _a._renderHTML = item._renderHTML;
          _a.hidden = !item.width;
          _a.value = row[v];
        return _a;
        
      }).filter(function (r) { return !!r; });
    });
    // column sort
    this.columnsData = modelWithIndex.map(function (row:any) {
      return row.sort(function (pre:any, next:any) { return pre.index - next.index; });
    });
    this.updateBodyHeight();
  }


  ngOnChanges(changes: SimpleChanges) {
    // not include model
    if (!changes || !changes.model)
      return;
    // first change
    if (changes.model.isFirstChange())
      return;
    this.model = changes.model.currentValue;
    this.transformModelData();
  }
}
